
package com.cib.ajax.rpc.serializer;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.json.JSONException;
import org.json.JSONObject;

import org.jabsorb.serializer.AbstractSerializer;
import org.jabsorb.serializer.MarshallException;
import org.jabsorb.serializer.ObjectMatch;
import org.jabsorb.serializer.SerializerState;
import org.jabsorb.serializer.UnmarshallException;

public class EnumSerializer extends AbstractSerializer {
	private static final long serialVersionUID = 1L;

	private static Class[] _JSONClasses = new Class[] { JSONObject.class };

	private Class[] serializableClasses;

	public Class[] getJSONClasses() {
		return _JSONClasses;
	}

	public Object marshall(SerializerState state,  Object p, Object o)
			throws MarshallException {
		try {
			for (Class<? extends Enum> clazz : getSerializableClasses()) {
				if (!clazz.isInstance(o))
					continue;
				Enum theEnum = clazz.cast(o);
				JSONObject jso = new JSONObject();
				jso.put("_name", theEnum.name());

				if (ser.getMarshallClassHints())
					jso.put("javaClass", theEnum.getClass().getName());

				BeanInfo info = Introspector.getBeanInfo(clazz);
				PropertyDescriptor props[] = info.getPropertyDescriptors();

				for (PropertyDescriptor prop : props) {
					if ("declaringClass".equals(prop.getName())
							|| "class".equals(prop.getName()))
						continue;

					Method readMethod = prop.getReadMethod();
					if (readMethod == null)
						continue;

					//if (ser.isDebug())
						//log.debug("invoking " + readMethod.getName() + "()");

					Object result;
					try {
						result = readMethod.invoke(o, (Object[]) null);
					} catch (Throwable e) {
						if (e instanceof InvocationTargetException)
							e = ((InvocationTargetException) e)
									.getTargetException();
						throw new MarshallException("bean "
								+ o.getClass().getName() + " can't invoke "
								+ readMethod.getName() + ": " + e.getMessage());
					}

					try {
						if (result != null || ser.getMarshallNullAttributes())
							jso
									.put(prop.getName(), ser.marshall(state, null,result,new Integer(0)));
					} catch (MarshallException e) {
						throw new MarshallException("bean "
								+ o.getClass().getName() + " " + e.getMessage());
					}
				}

				return jso;
			}
		} catch (IntrospectionException e) {
			e.printStackTrace();
			throw new MarshallException(
					"There was an exception while inspecting the class "
							+ o.getClass().getName() + ", see stdout");
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			throw new MarshallException(
					"There was an exception while inspecting the class "
							+ o.getClass().getName() + ", see stdout");
		} catch (JSONException e) {
			throw new MarshallException(e.getMessage());
		}
		throw new MarshallException("The specified object ("
				+ o.getClass().getName()
				+ ") was not an instance of any of the serializable classes");
	}

	@SuppressWarnings("unchecked")
	public Object unmarshall(SerializerState state, Class clazz, Object o)
			throws UnmarshallException {
		try {
			JSONObject jso = (JSONObject) o;
			String name = jso.getString("_name");
			if (name == null)
				throw new UnmarshallException("The specified JSONObject of type "
						+ clazz.getName() + " didn't contain a _name attribute");
			return Enum.valueOf(clazz, name);
		} catch (JSONException e) {
			e.printStackTrace();
			throw new UnmarshallException(e.getMessage());
		}
	}

	public ObjectMatch tryUnmarshall(SerializerState arg0, Class arg1,
			Object arg2) {
		return null;
	}

	/**
	 * @return the serializableClasses
	 */
	public Class[] getSerializableClasses() {
		return serializableClasses;
	}

	/**
	 * @param serializableClasses
	 *            the serializableClasses to set
	 */
	public void setSerializableClasses(Class[] serializableClasses) {
		this.serializableClasses = serializableClasses;
	}

}
